﻿// Copyright (c) 2014 Morten Bakkedal
// This code is published under the MIT License.

using System;

using FuncLib.DualFunctions.Expressions;

namespace FuncLib.DualFunctions.Compilation
{
	internal abstract class HessianLoopTerm : LoopTerm, IEquatable<HessianLoopTerm> // use IEqualityComparer<HessianLoopTerm> instead? (with hashcode and equals)
	{
		private SortedHessian hessian;

		public HessianLoopTerm(Expression expression, SortedHessian hessian)
			: base(expression)
		{
			this.hessian = hessian;
		}

		public static HessianLoopTerm Create(GeneratedFunction function, Expression expression, SortedHessian hessian)
		{
			return new HessianLoopTermFromHessian(function, expression, hessian);
		}

		public static HessianLoopTerm CreateFromGradient(GeneratedFunction function, Expression expression, SortedHessian hessian)
		{
			return CreateFromGradient(function, function, expression, hessian);
		}

		public static HessianLoopTerm CreateFromGradient(GeneratedFunction function1, GeneratedFunction function2, Expression expression, SortedHessian hessian)
		{
			return new HessianLoopTermFromGradient(function1, function2, expression, hessian);
		}

		public bool Equals(HessianLoopTerm other)
		{
			return hessian.Equals(other.hessian);
		}

		public override int GetHashCode()
		{
			return hessian.GetHashCode();
		}

		public SortedHessian Hessian
		{
			get
			{
				return hessian;
			}
		}

		private class HessianLoopTermFromHessian : HessianLoopTerm
		{
			private GeneratedFunction function;
			private string generatedCode;

			public HessianLoopTermFromHessian(GeneratedFunction function, Expression expression, SortedHessian hessian)
				: base(expression, hessian)
			{
				this.function = function;
			}

			public override void Prepare(FunctionBody body)
			{
				StorageOffsetsReference z = body.AddOffsets(hessian.GetIndexList(function.FunctionValueStorage.Index, function.Hessian, function.HessianStorage));
				generatedCode = "a[" + body.GetInnerFunctionIdentifier(function) + " + " + z.Identifier + "[i]]";
			}

			public override string GenerateArrayCode()
			{
				return generatedCode;
			}
		}

		private class HessianLoopTermFromGradient : HessianLoopTerm
		{
			private GeneratedFunction function1, function2;
			private string generatedCode;

			public HessianLoopTermFromGradient(GeneratedFunction function1, GeneratedFunction function2, Expression expression, SortedHessian hessian)
				: base(expression, hessian)
			{
				this.function1 = function1;
				this.function2 = function2;
			}

			public override void Prepare(FunctionBody body)
			{
				StorageOffsetsReference z1 = body.AddOffsets(hessian.GetIndexListGradient1(function1.FunctionValueStorage.Index, function1.Gradient, function1.GradientStorage));
				StorageOffsetsReference z2 = body.AddOffsets(hessian.GetIndexListGradient2(function2.FunctionValueStorage.Index, function2.Gradient, function2.GradientStorage));

				string s1 = "a[" + body.GetInnerFunctionIdentifier(function1) + " + " + z1.Identifier + "[i]]";
				string s2 = "a[" + body.GetInnerFunctionIdentifier(function2) + " + " + z2.Identifier + "[i]]";

				generatedCode = s1 + " * " + s2;
			}

			public override string GenerateArrayCode()
			{
				return generatedCode;
			}
		}
	}
}
